module DryScaffoldHelper
  include DryScaffold::Helper

  # Specify the color to be used in the header/footer of the DryScaffold list. (The nature of rico_corner makes this necessary.)
  #
  # Example:
  #   def scaffold_table_head_color
  #     "A5C1D7"
  #   end  
  def scaffold_table_head_color
    "005CB8"
  end

  # Override this method in your Helper to control display of CRUD operations, if you are using an Access Control mechanism.
  #
  # Example:
  #   def display_allowed?(action)
  #     @access.allowed_by_process?(action, access_handler, access_context)
  #   end
  def display_allowed?(action)
    true
  end

  # Override this function with your own localization routine, to localize column labels.
  def localize_label(scaffold_column, options = {})
  end
end

module DryScaffold
  def computed_field?(table_row, computing_method)
    # Look for it first in the Model then in a helper file
    table_row.respond_to?(computing_method) || self.respond_to?(computing_method)
  end
  
  def computed_field(table_row, computing_method)
    eval_value = ""
    if not computing_method.nil?
      table_row = eval("@#{table_row}") if table_row.is_a?(Symbol)
      case computing_method
        when Symbol
          # Look for it first in the Model
          if table_row.respond_to?(computing_method)
            eval_value = table_row.send computing_method
          else # then in the helper
            eval_value = self.send computing_method, table_row#eval "#{computing_method}(table_row)"
          end
        when String
          computing_method.gsub!("one_row", "table_row") unless computing_method.nil?
          eval_value = eval(computing_method)
        else
      end
    end
    eval_value
  end

  def like_clause_update(reset_filter = false)
    filter_name = "#{like_model_name(@scaffold_model)}"
    eval("@#{filter_name} = #{@scaffold_model}.new()")
    filter_by = eval("@#{filter_name}")
    for column in @scaffold_model.scaffold_columns
      include_array = nil
      # Rails initializes everything to defaults in the DB, we want it all to be blank, so we assign values ourselves
      filter_by[column.name] = nil
      restrict_method = "#{column.name}_custom_default_filter".to_sym
      if computed_field?(filter_by, restrict_method)
        filter_by[column.name] = computed_field(filter_by, restrict_method)
      end
      column_name = column.name
      filter_by[column_name] = params["#{filter_name}".to_sym][column_name.to_sym] unless params["#{filter_name}".to_sym].nil? or params["#{filter_name}".to_sym][column_name.to_sym].nil? or reset_filter
      if not filter_by[column.name].nil?        
        value = nil
        case column.display_type 
          when :filter_only
            like_comparison = true
            filter_method = "#{column.name}_custom_calculate_filter".to_sym
            if computed_field?(filter_by, filter_method)
              computed_field(filter_by, filter_method)
            end
          when :lookup
            like_comparison = false
            value = filter_by[column.name]
            # value = filter_by[column.name + "_"] unless column.auto_complete_options[:custom].nil?
          when :date, :date_time
            like_comparison = false
            value = filter_by[column.name].to_formatted_s
          else
            like_comparison = true
            value = filter_by[column.name].to_s
        end
        build_clause(@scaffold_model, column.name, value, :like => like_comparison) unless value.nil?
      end
      # restrict_method = "#{column.name}_custom_restrict_to_filter".to_sym
      # if computed_field?(filter_by, restrict_method)
      #   value = computed_field(filter_by, restrict_method)
      #   filter_by[column.name] = value unless value.nil?
      # end
    end
    restrict_to_filter_clause(filter_by)
    update_find_params_conditions
  end

  def restrict_to_filter_clause(filter_by)
    restrict_method = "#{@scaffold_model.to_s.underscore.to_sym}_custom_restrict_to_filter".to_sym
    if computed_field?(filter_by, restrict_method)
      computed_field(filter_by, restrict_method)
    end
  end
  
  def sort_clause_update(reset_filter = false)
    for column in @scaffold_model.scaffold_columns
      if params[:sort_by] and not reset_filter
        sort_name = sort_field_name(@scaffold_model, column)
        if params[:sort_by][sort_name]
          column.sort_order = params[:sort_by][sort_name]
        end
      else
        column.sort_order = (column.sortable == :desc or column.sortable == :asc) ? column.sortable.to_s : ""
      end
    end 
  end

  module Helper
  include DryScaffold::Common
  
    # Template files that can be customized are:
    # <tt>_form.rhtml</tt>
    # <tt>_list_body.rhtml</tt>
    # <tt>_list_row.rhtml</tt>
    def dry_template_location(view_file = "form")
    	pre_path = "/"
      if @options[:crud_model]
    	  view_folder = @options[:crud_model]
      	if @options[:controller] and @options[:controller]["/"]    	  
      	  pre_path << @options[:controller][0..@options[:controller].rindex("/")]
      	end
    	else
    	  view_folder = @options[:controller]
  	  end
    	view_path = pre_path + view_folder.to_s.tableize.singularize
    	view_path = pre_path + view_folder.to_s.tableize unless Dir["#{RAILS_ROOT}/app/views#{view_path}/_#{view_file}.rhtml"].length > 0
     	view_path = "shared" unless Dir["#{RAILS_ROOT}/app/views#{view_path}/_#{view_file}.rhtml"].length > 0
    	view_file = view_path + '/' + view_file
    	view_file      
    end
    
    def column_header_label(model, column, options = {})
      header = column.label      
      if !options[:raw_text_only]
  	    header, order_class = sort_field(model, column, options)
    	  tag_options = {}#{:width => column.size_in_list}
      end
      header = content_tag("div", header, :class => "sort_by") unless options[:hide_labels]
      return header, order_class
    end

    def list_field(row, field, options = {})
      list_field_by_type(row, scaffold_columns_hash[field], options)
    end

    def list_field_by_type(table_row, column, options = {})
      html = ''
      column_type = column.type          
      helper_method = "#{column.name}_custom_list_field".to_sym
      if computed_field?(table_row, helper_method)
        begin
          column_type = :custom
          html = computed_field table_row, helper_method
        rescue
          column_type = :text
        end
      end
      case column_type 
        when :custom
        when :boolean
          html = check_box_tag(nil, 1, table_row[column.name] || table_row[column.name] == 1, :disabled => true) unless options[:raw_text_only]
          html = table_row[column.name].to_s if options[:raw_text_only]
        when :date, :datetime :
          html = !table_row[column.name].nil? ? table_row[column.name].strftime('%Y-%m-%d') : ''
        else
          case column.display_type 
            when :phone
              html = number_to_phone(table_row[column.name].to_s)
            else
              html = table_row[column.name].to_s
          end
      end
      #FIXME 2006-12-21 Level=0 - Need to prevent column.ajax in :show action
      html = list_field_by_type_ajax(table_row, html, column) if column.ajax
      html = "&nbsp;" if html.nil? or html.length == 0 # IE needs something!
      html
    end

    def list_field_by_type_ajax(table_row, text, column)
      text = "-" if text.nil? or text.empty? # Give them something to click on
      options = {:content => {
                  :element => 'span',
                  :text => text,
                  :options => {
                    :id => "one_row_edit_" + table_row["id"].to_s + "_" + column.name}},
                 :url => {
                  :controller => params[:controller],
                  :model => @options[:scaffold_model].url_model,
                  :sub_model => @options[:scaffold_model].url_sub_model,
                  :action => 'update_one_field',
                  :field => column.name,
                  :id => table_row["id"]},
                 :ajax => {
                  :cancelText => "'Cancel'",
                  :okText => "'Update'"}}
      if column.type == :lookup
          helper_method = "#{column.name}_custom_select_options".to_sym
          options[:ajax][:collection] = "[" + computed_field(table_row, helper_method).map{ |key, value| "['#{key.to_s}', '#{value}']" }.join(",") + "]"
          options[:ajax][:value] = "'#{table_row[column.name]}'"
          options[:type] = :select
      end
      editable_content(options)
    end
    
#selectOptionsHTML
    # From Coda Hale, http://blog.codahale.com/2006/01/14/a-rails-howto-simplify-in-place-editing-with-scriptaculous/
    def editable_content(options)
      options[:content] = { :element => 'span' }.merge(options[:content])
      options[:url] = {}.merge(options[:url])
      options[:ajax] = { :okText => "'Save'", :cancelText => "'Cancel'"}.merge(options[:ajax] || {})
      script = Array.new
      case options[:type] 
        when :select
          script << "new Ajax.InPlaceCollectionEditor("
        else
          script << "new Ajax.InPlaceEditor("
      end
      script << "  '#{options[:content][:options][:id]}',"
      script << "  '#{url_for(options[:url])}',"
      script << "  {"
      script << options[:ajax].map{ |key, value| "#{key.to_s}: #{value}" }.join(", ")
      script << "  }"
      script << ")"
      content_tag(
       options[:content][:element],
       options[:content][:text],
       options[:content][:options]
      ) + javascript_tag( script.join("\n") )
    end

    def link_to_from_list(name, action, table_row, options = {}, html_options = {})
      link_options = {}
			page_num = nil
			if not @paginator.nil? and (@paginator.current.next or @paginator.current.previous)  
				page_num = @paginator.current.to_i
			end
			link_options[sort_limit_field_name(options[:scaffold_model]).to_sym] = sort_limit(options[:scaffold_model])
			link_options[:page] = page_num 
      if options[:parent_id]
        link_options[:action] = :view
  			link_options[:id] = options[:parent_id] 
  			link_options[:model] = options[:parent_model]
        link_options[:crud_action] = action
  			link_options[:crud_id] = table_row 
  			model = link_options[:crud_model] = options[:model]
		  else
        link_options[:action] = action
				link_options[:id] = table_row
				model = link_options[:model] = options[:scaffold_model]
		  end
		  # This is kind of cool, it will submit the link_options to the form that is associated with the filter sort.
		  java_script = "javascript: " 
		  java_script = "if (confirm('#{html_options[:confirm]}')) " if html_options[:confirm]
		  java_script << "Submit('#{form_filter_sort_by(model)}', '#{url_for(link_options)}')"
      link_to_function(name, java_script)
#      link_to(name, link_options, html_options)
    end

    def bullet
      "&nbsp;&#8226;&nbsp;"
    end

    def form_field(row, field, options = {})
      form_field_by_type(@scaffold_model, row, scaffold_columns_hash[field], options)
    end

    def form_field_by_type(model, row, column, options = {})
#      flash[:notice] = nil
      options.merge!(column.html_options)
      to_eval = nil
      html = ''
      column_type = column.display_type          
      select_options = []
      helper_method = "#{column.name}_custom_edit_field".to_sym
      computed_row = eval "#{model}.new()" # I have no idea why I need to this but otherwise, I can't get Rails to see a nested model on the .respond_to - this may be an engine or nested engine thing
      if computed_field?(computed_row, helper_method)
        html = computed_field(row, helper_method)
        column_type = :custom
      end
      case column_type
        when :custom
        when :boolean
          html = check_box row, column.name
        when :date
          html = calendar_tag row, column.name, column.sub_type, 
                   { :class => 'date',
                     :form_name => edit_model_name(model),
                     :include_blank => true, 
                     :use_month_numbers => true }, calendar_options.merge(options) rescue date_select(row, column.name)
          # html = datebocks_field(row, column.name,
          #           { :class => 'date',
          #               :button_title => 'Show calendar',
          #               :form_name => edit_model_name(model),
          #               :include_blank => true, 
          #               :use_month_numbers => true }, calendar_options.merge(options))
        when :datetime
          html = datetime_select(row, column.name)
        when :lookup
          helper_method = "#{column.name}_custom_select_options".to_sym
          html = select row, column.name, computed_field(row, helper_method)
        when :phone
					html = text_field row, column.name, {
                        :value => number_to_phone(@one_row[column.name].to_s),
                        :onblur => "PhoneDashAdd(this);return true;", 
          							:onchange => "NukeBadChars(this); return true;",
                        :size => 12 }
        when :password
          html = password_field row, column.name              
        when :text_area
          html = text_area(row, column.name, :cols => column.cols, :rows => column.rows)
        when :usa_state
          html = usa_state_select(row, column.name)
        when :country
          html = country_select(row, column.name, ["United States"])
        when :timezone
          html = time_zone_select(row, column.name)
        else
          if !DryScaffoldEngine.config(:upper_case_form_fields).nil? and DryScaffoldEngine.config(:upper_case_form_fields) and !column.name.include?("login") and !column.name.include?("email") and !column.name.include?("web_site")
            options[:onchange] ||= ''
            options.merge!(:onchange => options[:onchange] + "ToUpper(this); return true;") 
          end
          html = text_field row, column.name, options.merge(:size => column.size_in_edit)
      end
      html
      rescue Exception => err
        flash.now[:notice] ||= debug(err)
        if column.nil?
          flash.now[:notice] << "No column object provided"
        else
          flash.now[:notice] << "Unable to evaluate column:" + column.name
          flash.now[:notice] << debug(column)
          flash.now[:notice] << to_eval unless to_eval.nil?
        end
        ""
    end
    
    #TODO 2006-08-17 Level=1 - set year range dynamically
    def calendar_options(date = nil)
      options = { 
         :firstDay => 1,
         :range => [2000, 2020],
         :step => 1,
         :showOthers => true,
         :cache => true }
      options[:date] = date unless date.nil?
      options
    end

    def like_field(model, column, options = {})
      html = ''
      if not column.filterable or not model.filterable?
        html = '&nbsp'
      else
        filter_by = like_model_name(model)
        text_options = {:size => column.size_in_list, 
                   :maxlength => column.max_length}
        if options[:hide_filter] 
          html = hidden_field(filter_by, column.name, text_options)
        else
          column_type = column.type
          helper_method = "#{column.name}_custom_filter_field".to_sym
          filter_row = eval("@#{filter_by}")
          if computed_field?(filter_row, helper_method)
            begin
              value, hide_filter = computed_field(filter_row, helper_method)
              html = value
            rescue
            end
          end
          case column_type 
            when :boolean
              html = check_box filter_by, column.name
            when :custom
            when :filter_only
              unless hide_filter
                if not DryScaffoldEngine.config(:ajax_filter_fields).nil? and DryScaffoldEngine.config(:ajax_filter_fields)
                  html = like_field_with_auto_complete(model, filter_by, column.name, text_options, {:skip_style => true}, {:custom => true})
                else
                  html = text_field(filter_by, column.name, text_options)
                end
              end
            when :lookup
                html = select filter_by, column.name , value, :include_blank => true unless value.nil?
            # when :date, 
            #      :datetime
            #   date = filter_by[column.name].to_formatted_s unless filter_by[column.name].nil?
            #   @date_field = filter_by[column.name]
            #   html = calendar_tag filter_by, column.name, column.sub_type,
            #          { :class => 'date',
            #            :button_title => 'Show calendar',
            #            :form_name => form_filter_sort_by(model),
            #            :include_blank => true, 
            #            :use_month_numbers => true }, calendar_options(date) rescue date_select(filter_by, column.name, :include_blank => true, :use_month_numbers => true)
            else
              if not DryScaffoldEngine.config(:ajax_filter_fields).nil? and DryScaffoldEngine.config(:ajax_filter_fields)
                html = like_field_with_auto_complete(model, filter_by, column.name, text_options, :skip_style => true)
              else
                html = text_field(filter_by, column.name, text_options)
              end
          end
        end
      end
      html
    rescue Exception => err
      flash.now[:notice] ||= debug(err)
      if column.nil?
        flash.now[:notice] << "No column object provided"
      else
        # flash.now[:notice] << "Unable to evaluate column:" + column.name
        flash.now[:notice] << debug(column)
      end
      ""
    end
    
    def like_field_with_auto_complete(model, object, method, tag_options = {}, completion_options = {}, like_options = {})
      filter_by_method = "auto_complete_for_filter_by"
      # html = ''
      if like_options[:custom] == true
        filter_by_method << "_#{method}" 
      end
      text_field_with_auto_complete(object, method, tag_options, {:url => {:action => filter_by_method, :model => model, :filter => object, :column => method}}.update(completion_options))
    end

    def filter_url
      filter = {}
  		if params[:filter_by]
  		  params[:filter_by].each do |key, value|
  			  filter[:filter_by.to_s + "[" + key + "]"] = value
  			end
  		end
  		filter
    end  

    def sort_class(model, column, options = {})
      sortable = column.sortable && model.sortable?
      if sortable
        if column.sort_order.to_s.downcase == 'asc'
          order_class = 'asc'
          order = 'desc'
        elsif column.sort_order.to_s.downcase == 'desc'
          order_class = 'desc'
          order = ''
        else
          order_class = nil
          order = 'asc'
        end
      else
        order_class = nil
        order = 'asc'
      end
      return order_class, order
    end

    def sort_field(model, column, options = {})
      sortable = column.sortable && model.sortable?
      order_class, order = sort_class(model, column, options)
      caption = column.label
      sort_name = sort_field_name(model,column)
      html = ""
      if sortable 
        html << hidden_field(:sort_by, sort_name, {:value => column.sort_order}) 
      end
      if sortable and not options[:hide_filter] 
        html << content_tag("a", caption, 
                  :href => "javascript:SubmitFormField('" + :sort_by.to_s + "_" + sort_name + "','" + order + "','#{form_filter_sort_by(model)}');",
                  :class => order_class) 
      elsif not options[:hide_labels]
        html << content_tag("p", caption)
      end
      return html, order_class
      # debug column.field_label
    end

    def paginate_field(model, caption, page)
      html = ""
      html << content_tag("a", caption, 
                :href => "javascript:SubmitFormField('page','#{page.to_i}','#{form_filter_sort_by(model)}');") 
      html      
    end

    def format_time(time)
      time.strftime("%m/%d/%Y %I:%M %p")
    end 

    def format_date(date) 
      date.strftime("%m/%d/%Y")
    end 

    def column_empty?(column_value)
      column_value.nil? || (column_value.empty? rescue false)
    end
  
    def empty_column_text
      "-"
    end  

    # Return n non-breaking spaces.
    def nbsp(n)
      '&nbsp;' * n
    end

    # Return capitalized title.
    def titleize(title)
      title.split.map {|w| w.capitalize }.join(' ')
    end

    # Generates a temporary id for creating a new element
    def generate_temporary_id
      (Time.now.to_f*1000).to_i.to_s
    end 

    def column_sort_direction(column_name, params)
      column_name && current_sort_direction(params) == "asc" ? "desc" : "asc"
    end

    def column_class(one_row, scaffold_column, sort_column)
      column_value = eval(scaffold_column.eval) rescue nil
      class_name = String.new
      class_name += "empty " if column_empty?(column_value)
      class_name += "sorted " if (!sort_column.nil? && scaffold_column.name == sort_column)
      class_name
    end

    def column_label_with_punctuation(scaffold_column, options = {})
      scaffold_column ||= scaffold_columns_hash[options[:name]]
      if scaffold_column.type == :boolean
        punctuation = "?"
      else
        punctuation = ":"
      end
      column_label(scaffold_column, options) + punctuation
    end

    def column_label(scaffold_column, options = {})
      scaffold_column ||= scaffold_columns_hash[options[:name]]
      unless scaffold_column.type == :children
        if DryScaffoldEngine.config(:localization).nil? or !DryScaffoldEngine.config(:localization)
          scaffold_column.label
        else
          localize_label(scaffold_column, options)
        end
      end
    end

    def localize_label(scaffold_column, options = {})
      scaffold_column ||= scaffold_columns_hash[options[:name]]
      scaffold_column.label
    end
  
    def loading_indicator_tag(options)
      image_filename = "indicator.gif"
      "<img src=\"/images/#{image_filename}\" style=\"display: none;\" id=\"#{loading_indicator_id(options)}\" alt=\"loading indicator\" class=\"loading-indicator\" />"
    end  

    def num_columns_in_list
      n_columns = 0
      for column in @options[:scaffold_model].scaffold_columns
        if column.viewable_in_list?
			    n_columns += 1
			  end
		  end
		  n_columns
    end

    def scaffold_columns
      @options[:scaffold_model].scaffold_columns
    end

    def scaffold_columns_hash
      @options[:scaffold_model].scaffold_columns_hash
    end

    # The following are a bunch of helper methods to produce the common scaffold view id's

    def scaffold_content_id(options)
      "#{options[:scaffold_id]}-content"
    end

    def scaffold_column_header_id(options)
      "#{options[:scaffold_id]}-#{options[:column_name]}-column"
    end

    def scaffold_tbody_id(options)
      "#{options[:scaffold_id]}-tbody"
    end

    def scaffold_messages_id(options)
      "#{options[:scaffold_id]}-messages"
    end 

    def empty_message_id(options)
      "#{options[:scaffold_id]}-empty-message"
    end 

    def element_row_id(options)
      "#{options[:scaffold_id]}-#{options[:action]}-#{options[:id]}-row"
    end

    def element_cell_id(options)
      "#{options[:scaffold_id]}-#{options[:action]}-#{options[:id]}-cell"
    end

    def element_form_id(options)
      "#{options[:scaffold_id]}-#{options[:action]}-#{options[:id]}-form"
    end 

    def loading_indicator_id(options)
      if options[:id].nil?
        "#{options[:scaffold_id]}-#{options[:action]}-loading-indicator"
      else
        "#{options[:scaffold_id]}-#{options[:action]}-#{options[:id]}-loading-indicator"
      end
    end

    def element_messages_id(options)
      "#{options[:scaffold_id]}-#{options[:action]}-#{options[:id]}-messages"
    end 
  end
end

module ActionView::Helpers #:nodoc:
  module JavaScriptMacrosHelper #:nodoc:
  end

  module ActiveRecordHelper #:nodoc:
    def error_messages_for(object_name, options = {})
      options = options.symbolize_keys
      object = instance_variable_get("@#{object_name}")
      if object && !object.errors.empty?
        content_tag("div",
          content_tag(
            options[:header_tag] || "h2",
            "#{pluralize(object.errors.count, "error")} prohibited this #{options[:row_name] || object_name.to_s.gsub("_", " ")} from being saved"
          ) +
          content_tag("p", "There were problems with the following fields:") +
          content_tag("ul", object.errors.full_messages.collect { |msg| 
            msg = msg.split("--", 2)[1].strip if msg.include?("--")
            content_tag("li", msg) 
          }),
          "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
        )
      else
        ""
      end
    end
  end
end
